<template>
  <div ref="container" class="canvas-container">
    <div v-if="!drawing && !dragging" class="toolbar">
      <el-tooltip effect="dark" content="线" placement="left">
        <div class="toolbar-item" :class="{'check-toolbar': drawMode === 'line'}">
          <el-button type="text" class="el-icon-check toolbar-item-button" @click="setDrawMode('line')" />
        </div>
      </el-tooltip>
      <el-tooltip effect="dark" content="四边形" placement="left">
        <div class="toolbar-item" :class="{'check-toolbar': drawMode === 'rectangle'}">
          <el-button type="text" class="el-icon-crop toolbar-item-button" @click="setDrawMode('rectangle')" />
        </div>
      </el-tooltip>
      <el-tooltip effect="dark" content="刷新" placement="left">
        <div class="toolbar-item">
          <el-button type="text" class="el-icon-refresh toolbar-item-button" @click="refresh" />
        </div>
      </el-tooltip>
      <el-tooltip effect="dark" content="删除选择" placement="left">
        <div v-if="activePolygonIndex > -1" class="toolbar-item">
          <el-button type="text" class="el-icon-delete toolbar-item-button" @click="deletePolygon" />
        </div>
      </el-tooltip>
    </div>
    <canvas id="drawCanvas" ref="drawCanvas" :width="containerWidth" :height="containerHeight" />
  </div>
</template>

<script>
import { fabric } from 'fabric'
export default {
  name: 'CanvasDrawPolygon',
  props: {
    // 线宽
    lineWidth: { type: Number, default: 2 },
    // 背景图片
    backgroundImgUrl: { type: String, default: '' },
    // 点半径
    pointRadius: { type: Number, default: 2 },
    // 基础
    basicColor: { type: String, default: '#409EFF' },
    // 活跃时的颜色
    activeColor: { type: String, default: '#67c23a' },
    // 禁用时的颜色
    disabledColor: { type: String, default: '#909399' },
    // 线段颜色
    lineColor: { type: String, default: '#409EFF' },
    // 校正系数
    correctionFactor: { type: Number, default: 2 },
    // 背景颜色
    backgroundColor: { type: String, default: '#ff0000' }
  },
  data() {
    return {
      // 画布对象
      canvas: {},
      isEmpty: true,
      // 图片相对画图容器的放大比例
      zoomScale: 1,
      // 图片高度
      pictureHeight: 0,
      // 图片宽度
      pictureWidth: 0,
      // 画图模式
      drawMode: 'line',
      // 是否画图中
      clickDown: false,
      // 多边形点集
      polygons: [],
      // 当前激活的多边形索引
      activePolygonIndex: -1,
      // 当前激活多边形的激活点
      activePolygonPointIndex: -1,
      // 无效区域
      invalidRegion: false,
      // 绘制标识
      drawing: false,
      // 允许拖拽标识
      enableDrag: false,
      // 拖拽点
      draggingPoint: undefined,
      // 上一个点索引
      prevPointIndex: -1,
      containerWidth: 0,
      containerHeight: 0,
      // 定时器
      time: undefined,
      dragging: false
    }
  },
  computed: {
    canvasWidth() {
      return this.pictureWidth * this.zoomScale
    },
    canvasHeight() {
      return this.pictureHeight * this.zoomScale
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    /**
     * 初始化
     */
    init() {
      const that = this
      const container = this.$refs.container
      this.containerHeight = container.clientHeight
      this.containerWidth = container.clientWidth

      this.$nextTick(() => {
        const canvas = new fabric.Canvas('drawCanvas', {
          backgroundColor: this.backgroundColor,
          backgroundImg: this.backgroundImgUrl,
          selection: true,
          allowTouchScrolling: true,
          centeredRotation: true,
          centeredScaling: true,
          controlAboveOverlay: false,
          fireRightClick: true,
          preserveObjectStacking: false,
          selectionFullyContained: true,
          skipOffscreen: true
        })
        if (this.backgroundColor) {
          canvas.setBackgroundColor(this.backgroundColor, canvas.renderAll.bind(canvas))
        }
        const imgUrl = this.backgroundImgUrl
        if (imgUrl) {
          const center = canvas.getCenter()
          canvas.setBackgroundImage(imgUrl, canvas.renderAll.bind(canvas), {
            scaleX: 1,
            scaleY: 1,
            top: center.top,
            left: center.left,
            originX: 'center',
            originY: 'center'
          })
        }
        canvas.on({
          'mouse:down': (e, target) => {
            clearTimeout(this.time)
            this.time = setTimeout(() => {
              const x = e.pointer.x
              const y = e.pointer.y
              target = canvas.findTarget(e, true)
              if (target) {
                console.log(canvas.getActiveObject())
                console.log(target)
                this.clickObjectHandle(canvas, target, that.basicColor, that.activeColor)
                canvas.renderAll()
              } else {
                if (this.enableDrag) {
                  this.enableDrag = false
                  canvas.getObjects('circle').forEach(point => {
                    canvas.remove(point)
                  })
                  return
                }
                if (this.checkPointHover(canvas, x, y)) {
                  return
                }
                const circles = canvas.getObjects('circle')
                const point = circles[0]
                const pointCoincidence = point && point.containsPoint({ x, y })
                if (this.drawMode === 'line') {
                  if (this.drawing && pointCoincidence) {
                    this.completeDrawHandle(canvas, point, that.basicColor, that.activeColor)
                  } else if (this.drawing && !pointCoincidence) {
                    point.set({ left: e.pointer.x, top: e.pointer.y })
                    canvas.add(this.createLine(x, y, x, y))
                  } else {
                    this.drawing = true
                    canvas.skipTargetFind = true
                    canvas.add(this.createPoint(x, y), this.createLine(x, y, x, y))
                  }
                } else if (this.drawMode === 'rectangle') {
                  if (this.drawing && !pointCoincidence) {
                    this.completeDrawHandle(canvas, point, that.basicColor, that.activeColor)
                  } else if (!this.drawing) {
                    this.drawing = true
                    canvas.skipTargetFind = true
                    canvas.add(this.createPoint(x, y), this.createLine(x, y, x, y),
                      this.createLine(x, y, x, y), this.createLine(x, y, x, y), this.createLine(x, y, x, y))
                  }
                }
              }
            }, 100)
          },
          'mouse:move': e => {
            const x = e.pointer.x
            const y = e.pointer.y
            const isHover = this.checkPointHover(canvas, x, y)
            const circles = canvas.getObjects('circle')
            if (this.drawing) {
              const lines = canvas.getObjects('line')
              const point = circles[0]
              point.set({ left: e.pointer.x, top: e.pointer.y, fill: isHover ? this.disabledColor : this.basicColor })
              if (this.drawMode === 'line') {
                lines[lines.length - 1].set({ x2: x, y2: y })
              } else if (this.drawMode === 'rectangle') {
                lines[0].set({ x2: x })
                lines[1].set({ x1: x, x2: x, y2: y })
                lines[2].set({ x1: x, y1: y, y2: y })
                lines[3].set({ y1: y })
              }
              canvas.renderAll()
              return
            }
            if (this.drawing) {
              return
            }
            if (that.draggingPoint) {
              const beginX = that.draggingPoint.left
              const beginY = that.draggingPoint.top
              const polygons = canvas.getObjects('polygon') || []
              for (let i = 0; i < polygons.length > 0; i++) {
                const polygon = polygons[i]
                for (let j = 0; j < polygon.length > 0; j++) {
                  const point = polygon[j]
                  if (point.x === beginX && point.y === beginY) {
                    point.set({ x: x, y: y })
                    break
                  }
                }
              }
              canvas.renderAll()
              return
            }
            const point = circles.find(point => point.containsPoint({ x, y }))
            if (this.enableDrag && point) {
              this.draggingPoint = point
            }
          },
          'mouse:up': e => {
          },
          'mouse:dblclick': (e, target) => {
            clearTimeout(this.time)
            target = canvas.findTarget(e, true)
            if (target) {
              this.enableDrag = true
              target.points.forEach(point => {
                canvas.add(that.createPoint(point.x, point.y))
              })
              this.canvas.renderAll()
            }
          },
          'mouse:over': (e, target) => {
          },
          'mouse:out': (e, target) => {
          }
        })
        this.canvas = canvas
      })
    },
    /**
     * 完成绘制处理
     * @param canvas
     * @param point
     * @param basicColor
     * @param activeColor
     */
    completeDrawHandle(canvas, point, basicColor, activeColor) {
      const lines = canvas.getObjects('line')
      const points = []
      lines.map(item => {
        points.push({ x: item.x1, y: item.y1 })
        canvas.remove(item)
      })
      canvas.remove(point)
      const polygon = this.createPolygon(points)
      canvas.add(polygon)
      this.clickObjectHandle(canvas, polygon, basicColor, activeColor)
      canvas.skipTargetFind = false
      this.drawing = false
    },
    /**
     * 点击对象处理
     * @param canvas
     * @param item
     * @param basicColor
     * @param activeColor
     */
    clickObjectHandle(canvas, item, basicColor, activeColor) {
      canvas.getObjects('polygon').forEach(object => {
        object.set({ fill: basicColor })
      })
      item.set({ fill: activeColor })
    },
    /**
     * 检查点悬浮的对象
     * @param x
     * @param canvas
     * @param y
     * @return Boolean 结果
     */
    checkPointHover(canvas, x, y) {
      const polygons = canvas.getObjects('polygon') || []
      return polygons.length > 0 && polygons.find(item => item.containsPoint({ x, y })) !== undefined
    },
    /**
     * 生成圆点对象
     * @param x
     * @param y
     * @param option 配置
     */
    createPoint(x, y, option = {}) {
      return new fabric.Circle({
        radius: 5,
        originX: 'center',
        originY: 'center',
        left: x,
        top: y,
        selectable: false,
        fill: this.basicColor || option.backgroundColor
      })
    },
    /**
     * 生成线段对象
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @param option 配置
     */
    createLine(x1, y1, x2, y2, option = {}) {
      return new fabric.Line([x1, y1, x2, y2], {
        strokeWidth: this.lineWidth || option.lineWidth,
        selectable: false,
        stroke: this.basicColor || option.color
      })
    },
    /**
     * 生成多边形对象
     * @param points 点数组
     * @param option 配置
     */
    createPolygon(points, option = {}) {
      return new fabric.Polygon(points, {
        stroke: this.basicColor || option.lineColor,
        strokeWidth: 1 || option.lineWidth,
        fill: this.basicColor || option.backgroundColor,
        opacity: 0.3,
        hasBorders: false,
        objectCaching: false,
        hasControls: false
      })
    },
    /**
     * 设置画图模式
     * @param mode
     */
    setDrawMode(mode) {
      this.drawMode = this.drawMode === mode ? '' : mode
    },
    /**
     * 获取真实图片尺寸
     * @param imgData
     */
    getRealImageSize(imgData) {
      return new Promise((resolve, reject) => {
        const img = new Image()
        img.src = imgData
        img.onload = () => {
          if (img.width > 100) {
            const canvas = this.$refs.drawCanvas
            canvas.width = img.width
            canvas.height = img.height
            resolve({ width: img.width, height: img.height })
          } else {
            reject()
          }
        }
      })
    },
    /**
     * 刷新
     */
    refresh() {
      this.drawing = false
      this.dragging = false
      this.canvas.dispose()
    },
    /**
     * 删除多边形
     */
    deletePolygon() {
    }
  }
}
</script>

<style scoped lang='scss'>
  .canvas-container{
    overflow: hidden;
    height: 100%;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    //background-color: #ccc;
    position: relative;
    .toolbar {
      position: absolute;
      top: 10px;
      right: 10px;
      background: #FFFFFF;
      border-radius: 5px;
      width: 40px;
      overflow: hidden;
      z-index: 10;
      .toolbar-item {
        height: 36px;
        line-height: 36px;
        text-align: center;
        cursor: pointer;
        border-bottom: 1px solid #ccc;
        .toolbar-item-button {
          width: 100%;
        }
      }
      .toolbar-item:hover {
        background: #409EFF;
        .toolbar-item-button {
          color: #FFF;
        }
      }
      .toolbar-item:last-child {
        border-bottom: 0;
      }
      .check-toolbar {
        background: #409EFF;
        .toolbar-item-button {
          color: #FFF;
        }
      }
    }
    .operate-btn{
      display: none;
    }
    &:hover{
      .operate-btn{
        display: inline-block;
        position: absolute;
        left: 20px;
        top: 20px;
        i {
          color: #409EFF;
          cursor: pointer;
          font-size: 20px;
        }
      }
    }
    #drawCanvas{
      background-repeat: no-repeat;
      background-size: 100% 100%;
    }
  }
</style>
